Skip to content

[MySQL] Fix replication stalls caused by silently dropped idle connections - #760

Merged
bean1352 merged 17 commits into
mainfrom
fix/mysql-replication-freezes
Aug 25, 2026
Merged

[MySQL] Fix replication stalls caused by silently dropped idle connections#760
bean1352 merged 17 commits into
mainfrom
fix/mysql-replication-freezes

Conversation

@bean1352

@bean1352 bean1352 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

The PowerSync Service keeps two connections open to MySQL: one that streams changes, and a helper connection used for occasional lookups. The helper can sit idle for hours. Some firewalls quietly close connections that have been idle for an hour. The next time the PowerSync Service tries to use that closed connection, it waits ~15 minutes for the operating system to give up. During that wait replication is completely frozen, nothing is logged, and the PowerSync Service still reports itself as healthy. Shutdown has the same problem, because it sends one last query over that same connection and waits for the answer with no time limit. A self-hosted customer hit this issue five times in two days and traced it to their firewall's one hour idle timeout.

Fix

The MySQL connections now send a keepalive signal every 40 seconds. That is enough for a firewall to see the connection as active, so it never gets closed in the first place. Shutdown also gets a 5 second limit: if the final query gets no answer in that time, the service closes the connection itself instead of waiting.

AI disclaimer

I developed this change using Claude Opus 4.8 for the investigation and Claude Fable 5 for the implementation. I reviewed and tested the changes myself.

@changeset-bot

changeset-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a6370df

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@powersync/service-module-mysql Patch
@powersync/service-schema Patch
@powersync/service-image Patch
@powersync/service-core Patch
@powersync/service-module-convex Patch
@powersync/service-module-core Patch
@powersync/service-module-mongodb-storage Patch
@powersync/service-module-mongodb Patch
@powersync/service-module-mssql Patch
@powersync/service-module-postgres-storage Patch
@powersync/service-module-postgres Patch
test-client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@rkistner rkistner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, this specifically enables TCP keepalive, not a mysql-specific keepalive?

Is there an option on MySQL / Zongji to additionally do a mysql-level keepalive? If there is no built-in functionality for that, maybe something along the lines of a SELECT 1 query every minute?

Your work here is definitely an improvement, but the firewall silently dropping the connection due to being idle is not the only case this can happen, and TCP keepalives typically take very long to detect the issue on their own. A protocol-level keepalive with a socketTimeout or equivalent can be a much stronger check.

@bean1352

bean1352 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

To clarify, this specifically enables TCP keepalive, not a mysql-specific keepalive?

Is there an option on MySQL / Zongji to additionally do a mysql-level keepalive? If there is no built-in functionality for that, maybe something along the lines of a SELECT 1 query every minute?

Your work here is definitely an improvement, but the firewall silently dropping the connection due to being idle is not the only case this can happen, and TCP keepalives typically take very long to detect the issue on their own. A protocol-level keepalive with a socketTimeout or equivalent can be a much stronger check.

Yes the change was TCP keepalive only. There doesn't seem to be a built-in mysql-level keepalive in zongji or @vlasky/mysql, so I added one along the lines you suggested.

The service now creates the control connection itself and passes it into zongji, then runs SELECT 1 on it once a minute with a 5 second timeout. If the probe gets no answer, the listener destroys the connection and stops with an error, which goes through the normal restart path.

I'll also open a small types PR to powersync-mysql-zongji. PR: powersync-ja/powersync-mysql-zongji#19

@bean1352
bean1352 requested a review from Rentacookie August 20, 2026 13:54
@bean1352
bean1352 marked this pull request as ready for review August 24, 2026 07:27

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3edb4060bb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread modules/module-mysql/src/replication/zongji/BinLogListener.ts Outdated
Comment thread modules/module-mysql/src/replication/zongji/BinLogListener.ts Outdated
@michaelbarnes
michaelbarnes requested a review from rkistner August 24, 2026 14:48

@rkistner rkistner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me; just one comment on connection error handling.

It would be nice to get the relevant types upstream in our @powersync/mysql-zongji package, but it's not a blocker here.

Comment thread modules/module-mysql/src/replication/MySQLConnectionManager.ts Outdated
Comment thread .changeset/mysql-tcp-keepalive.md Outdated
Co-authored-by: Ralf Kistner <ralf@journeyapps.com>

@Rentacookie Rentacookie left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - thank you for handling the cleanups! 🙏

@bean1352
bean1352 merged commit d89195c into main Aug 25, 2026
46 checks passed
@bean1352
bean1352 deleted the fix/mysql-replication-freezes branch August 25, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants